package com.xpec.c4.net;

import java.nio.channels.ClosedChannelException;
import java.util.HashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.netty.bootstrap.Bootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.UpstreamMessageEvent;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;

import com.xpec.c4.codec.CodecUtility;
import com.xpec.c4.game.login.LoginModule;
import com.xpec.c4.game.login.db.LoginDAO;
import com.xpec.c4.game.time.TimeConstants;
import com.xpec.c4.game.utility.GUID;
import com.xpec.c4.game.utility.GUIDFactory;
import com.xpec.c4.game.utility.GUIDType;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.message.ConstantCode;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.message.login.CAccountLoginRequest;
import com.xpec.c4.message.login.CAccountRobotLoginRequest;
import com.xpec.c4.message.login.SAccountLoginRequest;
import com.xpec.c4.net.handler.ConsolidatingAutoFlusher;
import com.xpec.c4.service.common.ServiceInfo;
import com.xpec.c4.service.contexts.Contexts;
import com.xpec.c4.service.group.ServerGroup;
import com.xpec.c4.service.group.transportlayer.GroupMessage;
import com.xpec.c4.service.message.NetMessage;
import com.xpec.c4.service.module.ConsistentGroupName;
import com.xpec.c4.service.module.ServerProperties;
import com.xpec.c4.service.module.common.PlayerCommonInfoManager;
import com.xpec.c4.service.module.gateway.CGatewayCodeRequest;
import com.xpec.c4.service.server.CloseType;
import com.xpec.c4.service.server.ServerOperations;
import com.xpec.net.xnetty.cihper.BaseCipherChannelHandler;
import com.xpec.net.xnetty.cihper.ServerCipherChannelHandler;

public class XSENetNettyServer extends XSENetServer
{
	/**
	 * 登入時的 gateway code 保留時間
	 */
	public static final int DELAYMAXTIME = (int) (5 * TimeConstants.MILLI_SECOND_PRE_MINUTE); // 5分鐘內需登入

	/**
	 * 
	 */
	private Bootstrap mBootstrap;
	
	/**
	 * 
	 */
	protected Channel mChannel = null;
	
	/**
	 * 當前連線數, 非線上玩家數量
	 */
	private AtomicInteger connectCount = new AtomicInteger(0);

	/**
	 * 當前機器人連線數
	 */
	private AtomicInteger robotConnectCount = new AtomicInteger(0);

	/**
	 *  總歷史連線數
	 */
	private AtomicInteger historyConnectCount = new AtomicInteger(0);
	
	public XSENetNettyServer(int protocolType)
	{
		if( protocolType == XSENetConstant.PROTOCOLTYPE_TCP )
		{
			int processorNum = Runtime.getRuntime().availableProcessors();
				
			mBootstrap = new ServerBootstrap( new NioServerSocketChannelFactory(
							Executors.newCachedThreadPool(),
							new OrderedMemoryAwareThreadPoolExecutor(
								2 * processorNum, 
								0, 0) ) );
		}
	}
	
	public void setHandleMode(int mode)
	{
		if( mode == XSENetConstant.HANDLE_GATEWAYMODE )
			mBootstrap.setPipelineFactory( new XSENetGatewayPipelineFactory(name, this) );
	}
	
	public void start()
	{
		long bufSize = 1<<20;
		long childBufSize = 1<<20;
			
		/// TCP server
		mBootstrap.setOption("child.tcpNoDelay", true); /// 關閉 Nagle演算法
		mBootstrap.setOption("child.keepAlive", true);
		mBootstrap.setOption("child.sendBufferSize", childBufSize);
		mBootstrap.setOption("child.receiveBufferSize", childBufSize);
		mBootstrap.setOption("child.backlog", 1024);

		mBootstrap.setOption("tcpNoDelay", true);
		mBootstrap.setOption("broadcast", false);
		mBootstrap.setOption("sendBufferSize", bufSize);
		mBootstrap.setOption("receiveBufferSize", bufSize);
		mBootstrap.setOption("backlog", 1024);
			
		/// TCP server 除了這個 server channel, 每個 client 連進來就會開一個新的
		/// client channel (child channel)
		mChannel = ((ServerBootstrap)mBootstrap).bind( mAddress );
		
	}
	
	public void close()
	{
		mChannel.close().awaitUninterruptibly();
	}
	
	public boolean isStarted()
	{
		return mChannel.isOpen();
	}
	
	public void incrementOnlineUser()
	{
		connectCount.incrementAndGet();
		historyConnectCount.incrementAndGet();
	}
	
	public void decrementOnlineUser()
	{
		connectCount.decrementAndGet();
	}

	/**
	 * 增加機器人連線數量
	 */
	public void incrementOnlineRobot() {
		robotConnectCount.incrementAndGet();
	}

	/**
	 * 減少機器人連線數量
	 */
	public void decrementOnlineRobot() {
		robotConnectCount.decrementAndGet();
	}

	public int getConnectCount() {
		return connectCount.get();
	}	
	
	public int getHistoryConnectCount() {
		return historyConnectCount.get();
	}

	public int getRobotCount() {
		return robotConnectCount.get();
	}

	private XSENetSpeedometerFilter meter = new XSENetSpeedometerFilter();
	
	public XSENetSpeedometerFilter getMeter() 
	{
		return meter;
	}

	
}

/*********************************************************************
 * 
 * 
 *
 */
class XSENetGatewayPipelineFactory implements ChannelPipelineFactory 
{
	/**
	 * 是否使用计速器？
	 */
	private static final boolean enableSpeedFilter = ServerProperties.getBoolean("speedfilter", true);
	
	/**
	 * 是否使用消息加密？
	 */
	private static final boolean enableUseCipher = ServerProperties.getBoolean("useCipher", true);
	
	/**
	 * 是否為騰訊TGW接入
	 */
	private static final boolean enableTGWHandler = ServerProperties.getBoolean("useTGWHandler", true);
	
	private XSENetNettyServer parent = null;
	
	/**
	 * module name
	 */
	private String name;
	
	public XSENetGatewayPipelineFactory(String name, XSENetNettyServer parent) 
	{
		this.name = name;
		this.parent = parent;
	}
	
	@Override
	public ChannelPipeline getPipeline() throws Exception
	{
		ChannelPipeline p = Channels.pipeline();
		
//		if(!enableUseCipher){
//			p.addLast(ConsolidatingAutoFlusher.class.getSimpleName(), new ConsolidatingAutoFlusher());
//		}
//		
//		//rowan
//		p.addLast(XSENetSwfSecurityHandler.class.getSimpleName(), new XSENetSwfSecurityHandler());
//		
//		//是否為騰訊TGW接入
//		if(enableTGWHandler)
//		{
//			p.addLast(XSENetTGWHandler.class.getSimpleName(), new XSENetTGWHandler());
//		}
		
		// 是否使用流量统计？
		if( enableSpeedFilter ){
			p.addLast(XSENetSpeedometerFilter.class.getSimpleName(),parent.getMeter());
		}
		
		// 是否使用消息加密？
//		if(enableUseCipher){
//			p.addLast(BaseCipherChannelHandler.class.getSimpleName(), new ServerCipherChannelHandler());
//		}

		p.addLast(XSENetNettyDecoder.class.getSimpleName(), new XSENetNettyDecoder());
		p.addLast(XSENetNettyEncoder.class.getSimpleName(), new XSENetNettyEncoder());
//		p.addLast(XSENetLoginVerifyHandler.class.getSimpleName(), new XSENetLoginVerifyHandler(name,parent));
		
		/// 處理我們自己訊息的一定是最後一個 addLast 的
		p.addLast(XSENetGatewayChannelHandler.class.getSimpleName(), new XSENetGatewayChannelHandler(name, parent));
		
		return p;	
	}

}

/**
 * 
 *
 */
class XSENetGatewayChannelHandler extends SimpleChannelHandler 
{	
	private static final Log log = LogFactory.getLog(XSENetGatewayChannelHandler.class);
	
	private XSENetNettyServer parent = null;
	
	/**
	 * module name
	 */
	private String name;
	
	private String mAccountID;
	
	private static final String LOG_TRACE = "GATEWAY_LOG_TRACE:";
	
	private static final int[] WATCH_MESSAGES = new int[]
			{
		102003,
		111043,
		102019,
		102016,
		102051,
		116006,
		116003,
		116005,
		116012,
		116027,
		128001,
		125003,
		115014,
			};
	
	private static final HashMap<Integer, String> MESSAGES = new HashMap<Integer, String>();
	
	static{
		for (int messageType : WATCH_MESSAGES) {
			MESSAGES.put(messageType, ConstantCode.getNameWithCode(MessageTypes.class, messageType));
		}
	}
	
		
	public void setAccountID(String id)
	{
		mAccountID = id;
	}
	
	public XSENetGatewayChannelHandler(String name, XSENetNettyServer parent)
	{
		super();
		this.name = name;
		this.parent = parent;
	}
	
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) 
    {
    	Throwable ex = e.getCause();
    	if( !(ex instanceof ClosedChannelException) )
    	{
    		if(log.isErrorEnabled()){
    			//log.error(ctx, e.getCause());
    			log.error(e);
    		}
    	}
    	e.getChannel().close();
    }
  
	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
	{
		Object message = e.getMessage();
		if( message == null || !(message instanceof NetMessage) ) return;

		NetMessage msg = (NetMessage)message;
		
		int messageType = msg.getMessageType();
		
		// client送出字首为4的message表示不合法
		if( messageType / 100000 == 4 )
		{
			if(log.isWarnEnabled()){
				log.warn("wrong message type from client:" + e.toString());
			}
			return;
		}
		
		GUID sessionID = XSENetSessionPool.getSession(ctx.getChannel()).getID();
		
		if (MESSAGES.containsKey(messageType)) {
			// 在被監視的messageType中
			if (log.isInfoEnabled()) {
				if (sessionID == null) {
					log.info(LOG_TRACE + MESSAGES.get(messageType) + "|SessionId is null");
				} else {
					GUID playerId = PlayerCommonInfoManager.getInstance().getPlayerGuidBySession(sessionID);
					log.info(LOG_TRACE + MESSAGES.get(messageType) + "|sessionId=" + sessionID.toString() + "|playerId=" + (playerId == null ? "null" : playerId.toString()));
				}
			}
		}
		
		if(sessionID == null){
			return;
		}
		
		byte[] messageContentBytes = msg.getMessageContentBytes();
		String moduleID = msg.getModuleID();
		
		// 将client送来的twork Message转送给对应的Module
		GroupMessage gm = new GroupMessage();
		gm.setDestModuleID(moduleID);
		gm.setSessionID(sessionID);
		gm.setMessageType(messageType);
		gm.setMessageContentBytes(messageContentBytes);

		ServerGroup.getTransportManager().sendMessage(gm);
		ctx.sendUpstream(e);
	}
	
    /**
     * Invoked when a Channel is open, but not bound nor connected.
     */
    @Override
    public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
    {
    	ctx.sendUpstream(e);
    } 

    /**
     *  Invoked when a Channel is open and bound to a local address,
     *  but not connected.
     */
    @Override
    public void channelBound(ChannelHandlerContext ctx, ChannelStateEvent e)
    {
    	// ******捕鱼测试 產生 sessionID
    	GUID sessionID = GUIDFactory.createUUID(GUIDType.TYPE_SESSION);
    	
    	if(log.isInfoEnabled()){
    		log.info(LoginModule.LOGIN_TRACE_LOG_STRING+" Generate sessionID: " + sessionID);
    	}
    	
    	XSENetSession newSession = new XSENetSession(e.getChannel());
    	newSession.setID(sessionID);
    	XSENetSessionPool.addSession(e.getChannel(), newSession);
    	
    	// 這是供全域使用
		ServiceInfo.addSession(sessionID, newSession);
		// 單獨記一份在自己的Gateway內
		parent.gatewayModule.addSession(sessionID, newSession);
		
    	// ******捕鱼测试 產生 sessionID
    	
    	ctx.sendUpstream(e);
    }
    
    /**
     *  Invoked when a Channel was closed
     *  and all its related resources were released.
     */
    @Override
    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
    {
    	ctx.sendUpstream(e);
    } 
    
    /**
     *  Invoked when a Channel is open, bound to a local address
     *  and connected to a remote address.
     */
	@Override
	public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
	{
		this.parent.incrementOnlineUser();

		if (log.isInfoEnabled()) {
			log.info(GameUtility.getCurMethodName() + ": channel " + e.getChannel().getId());
		}
		
		try 
		{
			/**
			 *  session ID 改成 swf 驗證完畢後再做
			 */			
			ctx.sendUpstream(e);

			// channel要先記下來, 不然強迫斷線時不到人~
			XSENetSessionPool.addSession(e.getChannel(), null);
		}
		catch (Exception ex)
		{
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + ": Exception", ex);
			}
		}
	}
	
	/**
	 *  Invoked when a Channel was disconnected from its remote peer.
	 */
	@Override
	public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e)
	{
		this.parent.decrementOnlineUser();

		ctx.sendUpstream(e);
		
		Channel ch = e.getChannel();
		
		if (log.isInfoEnabled()) {
			log.info(GameUtility.getCurMethodName() + ": channel " + ch.getId());
		}

		GUID sessionID;
		try 
		{
			XSENetSession xseSession = XSENetSessionPool.getSession(ch);
			
			if(xseSession != null){
				sessionID = xseSession.getID();
			
				if(sessionID != null){
					
					CloseType closeType = ServerOperations.getSessionCloseType(xseSession);
					
					//斷現處理流程
					ServerOperations.disconnectionLocalProcess(sessionID, closeType, parent.gatewayModule);
					
					parent.gatewayModule.removeSession(sessionID);
				} else {
					// sessionID = null
					// 有建立連線，但沒有走到帳號登入就不會有 sessionID
					if (log.isErrorEnabled()) {
						log.error(GameUtility.getCurMethodName()
								+ ": session GUID not found from xseSession in channel " + ch.getId());
					}
				}

				Boolean isRobot = (Boolean) xseSession.getParameters().get(ServerOperations.IS_ROBOT);
				if (isRobot != null && isRobot) {
					this.parent.decrementOnlineRobot();
				}

				if (log.isInfoEnabled()) {
					log.info(GameUtility.getCurMethodName() + ": channel " + ch.getId() + " s:" + sessionID
							+ " disconnect proc finish.");
				}
			}
			else{
				// xseSession = null
				// 有建立連線，但沒有走到帳號登入就不會有 xseSession
				
				ServiceInfo.removeSession(xseSession);
				
				if (log.isErrorEnabled()) {
					log.error(GameUtility.getCurMethodName() + ": xseSession not found by channel " + ch.getId());
				}
			}
			XSENetSessionPool.removeSession(ch);
		} 
		catch (RuntimeException ex)
		{
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + ": RuntimeException", ex);
			}
		}
		catch (Exception ex)
		{
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + ": Exception", ex);
			}
		}

		// log.info(sessionID + " is closed!");
	}
  
}



class XSENetNettyEncoder extends OneToOneEncoder
{
	private static final Log log = LogFactory.getLog(XSENetNettyEncoder.class);

	private static final short START_FLAG = Short.MAX_VALUE;

	@Override
	protected Object encode(ChannelHandlerContext ctx, Channel channel, Object obj) throws Exception
	{
		if( !channel.isConnected() )
			return obj;
		
		/// 跟 NetMessage 非相關的東西就不 encode
		if( !(obj instanceof NetMessage) )
		{
			return obj;
		}
		
		NetMessage message = (NetMessage)obj;
		
		int messageType = message.getMessageType();
		byte[] bb = message.getMessageContentBytes();
		if( bb == null )
		{
			if (log.isWarnEnabled()) {
				log.warn("some thing error before encoding-bytes for message-type : "
						+ message.getMessageType());
			}
			bb = CodecUtility.contentEncode(message.getMessageContent(),messageType);
		}
		
		ChannelBuffer header = ChannelBuffers.buffer(2+4+4);
		header.writeShort(START_FLAG);
		header.writeInt(bb.length + 4);
		header.writeInt(messageType);

		ChannelBuffer body = ChannelBuffers.wrappedBuffer(bb);
		
		XSEPacketCategoryMeter.getInstance().recordOutput(messageType, bb.length+10);
		
		return ChannelBuffers.wrappedBuffer(header,body);
	}

}

class XSENetSwfSecurityHandler extends SimpleChannelHandler
{
	private static String responseXML = "<cross-domain-policy> <allow-access-from domain=\"*\" to-ports=\"*\"/></cross-domain-policy> \0";
	
	private boolean first = true;

	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
	{
		if( first )
		{
			first = false;
			
			ChannelBuffer channelBuffer = (ChannelBuffer) e.getMessage();

			String value = new String(channelBuffer.array());
			if (value.startsWith("<policy-file-request/>"))
			{
				Channel channel = e.getChannel();
				
				ChannelBuffer buffer = ChannelBuffers.buffer(responseXML.getBytes().length);
				buffer.writeBytes(responseXML.getBytes());

				channel.write(buffer);
				
				// 强制马上刷新发送
				ConsolidatingAutoFlusher flusher = (ConsolidatingAutoFlusher)channel.getPipeline().get("ConsolidatingAutoFlusher");
				if(flusher != null){
					//rowan:
					//flusher.flush();
				}
				
				channel.getPipeline().remove("XSENetSwfSecurityHandler");
				return;
			}
			else{
				first = true;
			}
		}
		ctx.sendUpstream(e);
	}

}

class XSENetLoginVerifyHandler extends SimpleChannelHandler
{
	private static final Log log = LogFactory.getLog(XSENetLoginVerifyHandler.class);

	private boolean first = true;
	
	private String name;
	
	private XSENetServer server = null;
	
	public XSENetLoginVerifyHandler(String name, XSENetServer _server)
	{
		super();
		this.name = name;
		this.server = _server;
	}
	
	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
	{
		//rowan:no new incomer
		if(false == server.isNewConnectionAllowed())
		{
			Channel channel = e.getChannel();
			channel.close();	
			first = false;
		}
		
		if( first )
		{
			first = false;
			
			Object message = e.getMessage();
			if( message != null && message instanceof NetMessage )
			{
				NetMessage msg = (NetMessage)message;
			
				int messageType = msg.getMessageType();
				if( messageType == MessageTypes.C_ACCOUNT_LOGIN_REQUEST )
				{
					if(log.isInfoEnabled()){
						log.info(LoginModule.LOGIN_TRACE_LOG_STRING+" recieve [C_ACCOUNT_LOGIN_REQUEST], msg: " + msg);
					}
					// gateway 收到 client 的登入要求
					//若是帳號登入, 則將帳號重新指定
					try
					{
						// 收到client第一个有效的Message后再开始建立 session id 和后续的对应数据
						Channel ch = e.getChannel();
//						String sessionID;
//
//						sessionID = name + "#"
//								+ DataTransfer.createSessionID(ch);
						
						//產生 sessionID
						GUID sessionID = GUIDFactory.createUUID(GUIDType.TYPE_SESSION);

						if(log.isInfoEnabled()){
							log.info(LoginModule.LOGIN_TRACE_LOG_STRING+" Generate sessionID: " + sessionID);
						}

						XSENetSession newSession = new XSENetSession(ch);
						newSession.setID(sessionID);
						XSENetSessionPool.addSession(ch, newSession);

						if (log.isInfoEnabled()) {
							log.info("create xseSession with channel=" + ch.getId() + " sessionId=" + sessionID);
						}

						
						// 這是供全域使用
						ServiceInfo.addSession(sessionID, newSession);
						// 單獨記一份在自己的Gateway內
						server.gatewayModule.addSession(sessionID, newSession);
		
						
						byte[] messageContentBytes = msg.getMessageContentBytes();
						
						CAccountLoginRequest reqClient = (CAccountLoginRequest) CodecUtility
								.decodeByte(messageContentBytes);
						
						// 識別 client 是否為機器人
						boolean isRobot = false;
						if (reqClient instanceof CAccountRobotLoginRequest) {
							CAccountRobotLoginRequest reqRobot = (CAccountRobotLoginRequest) reqClient;
							if (reqRobot.isRobot()) {
								isRobot = true;
							}
						}
						
						if (!isRobot) { // 不是機器人的話
							// 判斷是否能登入
							try {
								// 版本 => ver-XXX
								String[] platformSplit = reqClient.loginPlatForm.split(";");
								String version = platformSplit[2].split("-")[1];
								boolean isAllowVersion = ServerProperties.isAllowLoginClientVersion(version);
								if (!isAllowVersion) {
									if (log.isInfoEnabled()) {
										log.info(LoginModule.LOGIN_TRACE_LOG_STRING + " Not Allow Verison : " + version);
									}
									first = true;
									Channel channel = e.getChannel();
									channel.close();
									return;
								}
							} catch (Exception ee) {
								first = true;
								Channel channel = e.getChannel();
								channel.close();
								return;
							}
						}

						Contexts.startSessionContextMessage(sessionID, name, reqClient.clientIp,
								reqClient.loginPlatForm, "");
						
						newSession.getParameters().put(ServerOperations.IS_ROBOT, isRobot);

						// 增加機器人連線數量
						if (isRobot) {
							if (server instanceof XSENetNettyServer) {
								XSENetNettyServer nettyServer = (XSENetNettyServer) server;
								nettyServer.incrementOnlineRobot();
							}
						}

						if (log.isInfoEnabled()) {
							log.info("client login request: login=" + reqClient.login + " code=" + reqClient.code
									+ " token=" + reqClient.reconnectToken);
						}
						
						// get gateway code from redis
						CGatewayCodeRequest verifyData = LoginDAO.getGatewayCodeFromRedis(reqClient.code);

						if(verifyData != null){
							if(log.isInfoEnabled()){
								log.info(LoginModule.LOGIN_TRACE_LOG_STRING + " "
										+ server.getGatewayModule().getModuleName() + " a: " + verifyData.account
										+ " check verifyCode from client, verifyCode: " + verifyData.code);
							}
						}
						
//						String loginModule = ConsistentGroupName
//								.lookUp(ModuleName.GROUP_LOGIN, verifyData.account);
						
						SAccountLoginRequest reqServer = new SAccountLoginRequest();
						
						// 找不到verifyData, 可能是透過f5刷新, 丟到login去做檢查好了
						if(verifyData == null){
							if(log.isWarnEnabled()){
								log.warn("VerifyCode is null! " + reqClient.code);
							}
							if (GameUtility.isHaveString(reqClient.reconnectToken)) {
								reqServer.account = null;
								reqServer.code = reqClient.code;
								reqServer.reconnectToken = reqClient.reconnectToken;
								reqServer.playerIp = reqClient.clientIp;
								
								messageContentBytes = CodecUtility.encodeByte(
										reqServer,
										MessageTypes.S_ACCOUNT_LOGIN_REQUEST);
								msg = new NetMessage();
								msg.setSessionID(sessionID);
								msg.setMessageContentBytes(messageContentBytes);
								msg.setMessageType(MessageTypes.S_ACCOUNT_LOGIN_REQUEST);
								msg.setModuleID(reqClient.login);
								e.getChannel().getPipeline().remove("XSENetLoginVerifyHandler");
								ctx.sendUpstream(new UpstreamMessageEvent(e
										.getChannel(), msg, e
										.getRemoteAddress()));
								return;
							}
						}
						else if(verifyData.time + XSENetNettyServer.DELAYMAXTIME >= System.currentTimeMillis()){
							// verifyData 還沒有過期
							
							// 找出 account id 對應的 login module
							String loginModule = ConsistentGroupName.lookUpLoginModule(verifyData.account,
									verifyData.robot);
							
							reqServer.account = verifyData.account;
							reqServer.code = reqClient.code;
							reqServer.playerIp = reqClient.clientIp;					
							
							messageContentBytes = CodecUtility.encodeByte(
									reqServer,
									MessageTypes.S_ACCOUNT_LOGIN_REQUEST);
							msg = new NetMessage();
							msg.setSessionID(sessionID);
							msg.setMessageContentBytes(messageContentBytes);
							msg.setMessageType(MessageTypes.S_ACCOUNT_LOGIN_REQUEST);
							msg.setModuleID(loginModule);
							e.getChannel().getPipeline().remove("XSENetLoginVerifyHandler");
							ctx.sendUpstream(e);
							ctx.sendUpstream(new UpstreamMessageEvent(e.getChannel(),msg,e.getRemoteAddress()));
							return;
						}
						else{
							if (log.isWarnEnabled()) {
								log.warn("VerifyCode is timeout! time:"
										+ verifyData.time + " now:"
										+ System.currentTimeMillis());
							}
						}
						
						if (log.isWarnEnabled()) {
							log.warn("client login with invalid-code and disconnect : "
									+ reqClient.code);
						}
						
					}
					catch(Exception ex)
					{
						if(log.isErrorEnabled()){
							log.error("C_ACCOUNT_LOGIN_REQUEST fail", ex);
						}
					}
				}
			}
		}

		//rowan: not know why this?
		//非法連線
		first = true;
		Channel channel = e.getChannel();
		channel.close();

		return;
	}
}
